﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using MyUtils;

public static class TestHelper {
	public static void CheckThreadSafity(this Action func) {
		CheckThreadSafity(func, 5, 5);
	}

	/// <summary>
	/// Run N threads simultaniusly, wait for finishing, and rethrow exceptions.
	/// Very usefull for thread safe testing.
	/// </summary>
	/// <param name="func">function to execute</param>
	/// <param name="threadCount">threads count</param>
	/// <param name="experimentsCount">run count per each test</param>
	public static void CheckThreadSafity(this Action func, int threadCount, int experimentsCount) {
		for (int k = 0; k < experimentsCount; k++) {
			Exception lastThreadException = null;
			var threads = new List<Thread>();

			var threadBody = new ThreadStart(delegate {
				try {
					func.Invoke();
				} catch (Exception ex) {
					lastThreadException = ex;
				} finally {
					lock (threads) {
						threads.Remove(Thread.CurrentThread);
					}
				}
			});

			for (int i = 0; i < threadCount; i++) {
				var thread = new Thread(threadBody) {
					IsBackground = true,
					Name = string.Format("ThreadSafeTester #{0} Experiment {1}", i + 1, k + 1),
				};
				lock (threads) {
					threads.Add(thread);
				}
				thread.Start();
			}
			while (threads.Exists()) // actively waiting for threads
				Thread.Sleep(100);

			if (lastThreadException != null) {
				throw new Exception(
					"\r\n\r\nTHREAD UNSAFE (Experiment #" + (k + 1) + " with " + threadCount +
						" threads). One of threads throws exception\r\n", lastThreadException);
			}
		}
	}
}

[Serializable]
public class ThreadUnsafeException : Exception {
	public ThreadUnsafeException() {}

	public ThreadUnsafeException(string message) : base(message) {}

	public ThreadUnsafeException(string message, Exception inner) : base(message, inner) {}
}